#ifndef __KORE2_H
#define __KORE2_H

#define KORE2_MAJOR 2
#define KORE2_MINOR 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <math.h>
#include <map>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cassert>

using std::string;
using std::map;
using std::vector;

#include <SDL/SDL.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include <SDL/SDL_mutex.h>
#include <SDL/SDL_mixer.h>
#include <SDL/SDL_sound.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_gfxPrimitives.h>
#include <sigc++/sigc++.h>

/// Define DISABLE_OPENGL to disable compiling against OpenGL
#ifndef DISABLE_OPENGL
	#include <GL/gl.h>
	#include <GL/glu.h>
	#include <GL/glext.h>
#endif

/**
 * @namespace kore
 * @brief The main kore namespace.
 * 
 * Kore2 Engine, an object oriented game development framework loosely based
 * on the Kore framework by Seoushi. Kore2 is developed by Noccy Labs and is
 * copyright (c) 2010, Noccy Labs. Distributed under the GNU GPL version 3.
 *
 * @copyright (c) 2010, Noccy Labs
 * @copyright Parts (c) 2005 Seoushi Games
 * @license GPL v3
 */
namespace kore
{

	// Predeclare these
	class KoreEngine;
	class KoreContext;
	class ActorManager;
	class Actor;
	class SceneManager;
	class KoreCamera;
	class ConfigFile;

	/**
	 * Cursor mode enumeration, when set to software cursor a call has to be
	 * made to setCursor to set the cursor.
	 */
	enum ECursorMode
	{
		CM_HIDDEN,			///< No cursor
		CM_HARD,			///< Hardware cursor
		CM_SOFT				///< Software cursor
	};

	/**
	 * @class KoreApplication
	 * @brief Base class for a kore2 Application
	 *
	 * Base class for a kore2 Application. Remember to call on the constructor
	 * with the actual application arguments.
	 *
	 * @example koreapplication.cpp
	 */
	class KoreApplication
	{
		public:
							KoreApplication(int argc, char** argv);
							~KoreApplication();
			int				run();
			KoreEngine*		engine;
			ActorManager*	actormanager;
			SceneManager*	scenemanager;
			void			setCursorMode(ECursorMode mode);
			int				getCursorMode();
			int				setPrimaryContext(KoreContext* context);
			KoreContext*	getPrimaryContext();
			ConfigFile*		config;

			sigc::signal<void,long>	key_pressed;
			sigc::signal<void,long>	key_down;
			sigc::signal<void,long>	key_up;
		private:
			long			setDesiredFramerate(long framerate);
			long			getActualFramerate();
			long			getTicks();
			void			sleep();

	private:
			int				m_cursormode;
			KoreContext*	mp_primarycontext;
			long			m_InitFlags;
			long			m_DesiredFPS; // (m_DesiredFPS)
			long			m_DelayMs; // (m_DesiredFPS/1000)
			long			m_LastSleepMs; // Last sleep time in ms
			long			m_ActualFPS; // Actual framerate
			long			m_ActualFPSCount; // Frames rendered last second
			long			m_ActualFPSTicks; // Ticks to measure from
	};
	

	/**
	 * @class KoreEngine
	 *
	 *
	 *
	 *
	 */
	class KoreEngine
	{
		public:
							KoreEngine();
							~KoreEngine();
			int				registerCommandHandler(const char* cmd, void* handler);
			int				unregisterCommandHandler(int hcommand);
	};


	/**
	 * Enum containing the states available for an actor.
	 */
	enum ActorState
	{
		AS_EMPTY, 			/*!< Actor not yet initialized */
		AS_IDLE, 			/*!< Actor is currently idle */
		AS_RUNNING,			/*!< Actor is currently running */
		AS_SLEEPING			/*!< Actor is sleeping */
	};
	

	/**
	 * @class ActorManager
	 * @brief
	 *
	 *
	 *
	 */
	class ActorManager
	{
		public:
							ActorManager();
							~ActorManager();
			int				registerActor(Actor* actor);
			int				removeActor(Actor* actor);
	};
	

	/**
	 * @class Actor
	 * @brief
	 *
	 *
	 *
	 */
	class Actor
	{
		public:
							Actor();
							~Actor();
			int				getState();
			virtual int		invokeEvent(string event) = 0;
		private:
			bool			m_sleeping;
			ActorState		m_state;
	};
	

	#if 0 // Event code (Disabled)
	/**
	 * @class KoreBind
	 * @brief
	 *
	 *
	 *
	 */
	class KoreBind
	{
		public:
							KoreBind();
							~KoreBind();
	};


	/**
	 * @class KoreInput
	 * @brief
	 *
	 *
	 *
	 */
	class KoreInput
	{
		public:
							KoreInput();
							~KoreInput();
			KoreEvent&		peekEvent();
			KoreEvent&		popEvent();
	};
#endif

	/**
	 * @class Process
	 * @brief
	 *
	 *
	 *
	 */
	class Process
	{
		
	};
	

	/**
	 * @class ProcessManager
	 * @brief
	 *
	 *
	 *
	 */
	class ProcessManager
	{
		
	};
	

	/**
	 * @class Scene
	 * @brief
	 *
	 *
	 *
	 */
	class Scene
	{
		public:
							Scene(KoreApplication* app);
			virtual			~Scene();
			virtual void	run();
			KoreCamera*		camera;
	};
	

	/**
	 * @class SceneManager
	 * @brief
	 *
	 *
	 *
	 */
	class SceneManager
	{
		public:
							SceneManager();
							~SceneManager();
			void			addScene(Scene* scene);
			void			removeScene(Scene* scene);
			Scene*			popScene();
			void			run();
			void			removeAllScenes();
		private:
			vector<Scene*>	m_scenes;
	};
	
	
	/**
	 * Enum holding the base collision detection algorithms
	 */
	enum ECollision {
		OCD_NONE = 0,	///< No collision detection
		OCD_PIXEL,		///< Pixel collision detection
		OCD_BBOX,		///< Bounding box collision detection
		OCD_CIRCLE,		///< Circle collision detection
		OCD_CUSTOM		///< Custom collision detection
	};

	/**
	 * @class KoreCamera
	 * @brief Translates coordinates in 2D or 3D space
	 *
	 * 
	 */
	class KoreCamera
	{
		public:
						KoreCamera(float x=0.0f, float y=0.0f, float z=0.0f);
						~KoreCamera();
			float		translateX(float x);
			float		translateY(float y);
			float		translateZ(float z);
			float		getX();
			float		getY();
			float		getZ();
			void		setCamera(float x, float y, float z=0.0f);
		private:
			float		m_camerax; ///< Current X coordinate
			float		m_cameray; ///< Current Y coordinate
			float		m_cameraz; ///< Current Z coordinate
	};
	
	enum EContextType
	{
		CT_NONE = 0,
		CT_2D,
		CT_3D
	};

	/**
	 * @class KoreContext
	 * @brief
	 *
	 *
	 *
	 */
	class KoreContext
	{
		public:
								KoreContext();
			virtual int			attachPrimaryContext();
			virtual int			detachPrimaryContext();
			virtual void		setParameters(Uint16 width, Uint16 height, Uint16 bpp);
			virtual void		setFullscreen(bool fullscreen);
			virtual void		refresh();
		protected:
			Uint16				m_width;		///< Width of context
			Uint16				m_height;		///< Height of context
			Uint16				m_bpp;			///< Bits per pixel to use
			bool				m_isContextAttached; ///< Attached state
			bool				m_isFullScreen; ///< Fullscreen state
	};

	class Kore2DContext : public ::kore::KoreContext
	{
		public:

	};

	class Kore3DContext : public ::kore::KoreContext
	{
		
	};

	class ConfigFile
	{
		public:
								ConfigFile(string file);
								~ConfigFile();
			bool				getBool(string group, string key, bool defval = false);
			Sint16				getInt(string group, string key, int defval = 0);
			string				getString(string group, string key, string defval = "");
			void				setBool(string group, string key, bool value);
			void				setInt(string group, string key, int value);
			void				setString(string group, string key, string value);
			void				deleteKey(string group, string key);
			void				deleteGroup(string group);
			void				save();
		private:
			string				m_configfile;
			map<string,string>	m_config;
	};

	/**
	 * @namespace sdl
	 * @brief Contains all SDL-related functionality
	 */	
	namespace sdl
	{
		class SdlSurface;
		class SdlContext : public ::kore::Kore2DContext
		{
			public:
				int				attachPrimaryContext();
				int				detachPrimaryContext();
				void			setParameters(Uint16 width, Uint16 height, Uint16 bpp);
				void			setFullscreen(bool fullscreen);
				void			assignSurface(SdlSurface* surface);
				void			refresh();
		};
		class SdlSurface
		{
			public:
								SdlSurface(int width, int height);
								SdlSurface(SDL_Surface* surface);
								~SdlSurface();
				SDL_Surface*	getSurface();
				void			setPixel(int x, int y, Uint32 color);
				void			clear(Uint32 color);
				void			blit(SDL_Surface* s);
				void			blitRegion(SDL_Surface* s, int x, int y, int width, int height);
			private:
				SDL_Surface*	m_surface;
				int				m_width;
				int				m_height;
		};
		class Compositor
		{
		public:
								Compositor();
								~Compositor();
			int					addLayer(SdlSurface* surface);
			int					addLayer(SDL_Surface* surface);
			float				getLayerOpacity(int layer);
			void				setLayerOpacity(int layer, float opacity);
			void				getLayerRect(int layer, SDL_Rect& rect);
			void				setLayerRect(int layer, SDL_Rect& rect);
			void				refresh(SDL_Surface* surface);
		private:
			vector<SdlSurface*> layers;
			SDL_Surface*		m_surface;
		};
	};
	
	/**
	 * @namespace opengl
	 * @brief Contains all OpenGL-related functionality
	 */	
	namespace opengl
	{
		class OpenglContext : public ::kore::Kore3DContext
		{
			
		};
	};

	/**
	 * Resource handle, holds data and length.
	 */
	struct Resource {
		char			type;		///< Resource type
		char*			pdata;		///< Pointer to the resource data
		Uint32			len;		///< Length of the data
	};

	/**
	 * Resource flags, combine to make up the flags of the ResourceHeader.
	 */
	enum EResourceFlags
	{
		RHF_MMAP			= 1,	///< File should be memory mapped
		RHF_COMPRESSED		= 2		///< Index is compressed
	};

	/**
	 * Resource type hints. 
	 */
	enum EResourceTypes
	{
		RT_UNKNOWN			= 0,	///< Raw content, not handled by kore2
		RT_SOUND			= 10,	///< Sound file (raw)
		RT_SOUND_MP3		= 11,	///< Sound file (mp3)
		RT_SOUND_OGG		= 12,	///< Sound file (ogg)
		RT_SOUND_OTHER		= 19,	///< Sound file (other)
		RT_SPRITEMAP		= 20,	///< Sprite map
		RT_IMAGE			= 30,	///< Image (raw)
	};

	/**
	 * Encapsulates the header of a resource file, 64 bytes
	 */
	struct ResourceHeader
	{
		char			version;		///< Version of file
		char			reserved[3];	///< Reserved
		Uint16			resourcecount;	///< Number of resources in file
		Uint16			flags;			///< Flags (RHF_*)
		Uint32			indexoffset;	///< Offset to index
		Uint32			indexlength;	///< Length of index
		Uint32			indexcompressed;///< Compressed length of index
		char			padding[44];	///< Padding up to 64 bytes
	};
	/**
	 * Encapsulates the header of a specific resource item, 274 bytes
	 */
	struct ResourceIndex
	{
		char			resourcetype;	///< Resource type hint (RT_*)
	    char			reserved[3];	///< Reserved
		Uint32			hash;			///< Hash (for indexing)
		Uint32			fileoffset;		///< Offset to file data
		Uint32			filelength;		///< Length of file data
		Uint32			filecompressed;	///< Compressed length of file
		char			filename[256];	///< Filename (relative to root)
	};

	/**
	 * @class ResourceFile
	 * @brief Loads and accesses resource files
	 * 
	 * Wraps a resource file and gives access to its content.
	 */
	class ResourceFile
	{
		public:
							ResourceFile(string filename = "");
							~ResourceFile();
			Resource*		getResource(Uint32 resourceid);
			Resource*		getResource(string resourcename);
			int	 			openResourceFile(string filename);
			int				instancecount;
			int				status;
		private:
			FILE*			m_file;
	};
	
	/**
	 * @class ResourceManager
	 * @brief Pools loaded resources
	 * 
	 * The resource manager takes care of loading and unloading resource files
	 * containing various kinds of assets. It is to be accessed using the
	 * instance() method since it is implemented as a singleton class.
	 *
	 * @singleton
	 */
	class ResourceManager
	{
		public:
								ResourceManager();
								~ResourceManager();
			int					load(string key, string filename);
			int					unload(string key);
			void				addRawResource(string key, void* resource);
			void*				getRawResource(string key);
			ResourceFile*		getResourceFile(string key);
			/**
			 * Return the existing instance (or create a new one if there is
			 * none existing). Singleton accessor.
			 *
			 * @return ResourceManager* The resource manager instance
			 */
			static ResourceManager* instance()
			{
				static ResourceManager* instance;
				if (!instance)
				{
					instance = new ResourceManager();
				}
				return instance;
			}
		private:
			::std::map<string,ResourceFile*> resources;
	};

	/**
	 * @namespace sound
	 * @brief Contains all the sound-related functionality
	 *
	 * Makes good use of SDL Mixre and SDL Sound.
	 */
	namespace sound
	{
		class MixerChannel;

		const int MAX_CHANNELS = 32;
		const int FIRST_CHANNEL = 1;
		const int MAX_GROUPS = 8;

		/**
		 * @brief Enumeration of all the available mixer channels and
		 * meta channels.
		 *
		 * This include channel groups, music channel, master channel
		 * and all the effects channel. Please note that not all
		 * functionality is available for all channels! The music-related
		 * functionality for example is only available on the CHAN_MUSIC
		 * channel.
		 */
		enum EChannels
		{
			/// Music channel
			CHAN_MUSIC = -1,
			/// Master channel
			CHAN_MASTER = 0,
			/// The sound channels, room for 63 channels total.
			CHAN_SOUND01 = 1, CHAN_SOUND02, CHAN_SOUND03, CHAN_SOUND04,
			CHAN_SOUND05, CHAN_SOUND06, CHAN_SOUND07, CHAN_SOUND08,
			CHAN_SOUND09, CHAN_SOUND10, CHAN_SOUND11, CHAN_SOUND12,
			CHAN_SOUND13, CHAN_SOUND14, CHAN_SOUND15, CHAN_SOUND16,
			/// All channels (CHAN_SOUND01->CHAN_SOUND16)
			CHAN_ALL = 65,
			/// Channel groups 1 to 8
			CHAN_GROUP1 = 128, CHAN_GROUP2, CHAN_GROUP3, CHAN_GROUP4,
			CHAN_GROUP5, CHAN_GROUP6, CHAN_GROUP7, CHAN_GROUP8
		};

		/**
		 * @class Mixer
		 * @brief Mixer instance
		 *
		 * ...
		 *
		 * @singleton
		 */
		class Mixer
		{
			public:
									Mixer();
									~Mixer();
				void                setChannelVolume(EChannels channel, unsigned char volume);
				unsigned char       getChannelVolume(EChannels channel);
				int                 setMixerParameters();
				MixerChannel*       getMixerChannel(EChannels channel);
				static Mixer*       instance()
				{
					static Mixer* instance;
					if (!instance)
					{
						instance = new Mixer();
					}
					return instance;
				}
			private:
				MixerChannel*		m_channels[MAX_CHANNELS]; // TODO: Bad magic
		};

		/**
		 * @class MixerChannel
		 * @brief Provides unified access to a single mixer channel or
		 * channel group.
		 *
		 * It does this by wrapping all sound-related functionality in
		 * a nice package. This class is not intended to be used via its
		 * constructor unless you know what you're doing. Instead, use
		 * kore::sound::Mixer::getMixerChannel.
		 *
		 * @see kore::sound::Mixer::getMixerChannel()
		 */
		class MixerChannel
		{
			public:
									MixerChannel(EChannels channel);
									~MixerChannel();
				int                 loadMusic(::std::string file);
				int                 loadMusic(::kore::Resource resource);
				int                 play(int loop = 1);
				void				setChannelGroup(int group);
				int					getChannelGroup();
			private:
				EChannels           m_channel;	///< Number of channels
				int					m_group;	///< The group this channel is part of
				Mix_Music*          m_music;	///< Pointer to music
				Mix_Chunk*          m_sound;	///< Pointer to sound chunk
		};

	};

	/**
	 * @namespace ui
	 * @brief User Interface classes
	 *
	 * The classes in the UI namespace is used to access all kind of interface
	 * related rendering, including fonts and windows.
	 */
	namespace ui
	{
		class Font
		{
			public:
				Font(const char* fontface, int fontsize);
				~Font();
				void draw(SDL_Surface* surface, int x, int y, const char* text, SDL_Color color);
				void drawShaded(SDL_Surface* surface, int x, int y, const char* text, SDL_Color color, SDL_Color bgcolor);
				void drawBlended(SDL_Surface* surface, int x, int y, const char* text, SDL_Color color);
			private:
				TTF_Font* m_pFont;
		};
		enum EElementHittest
		{
			HIT_NONE = 0,
			HIT_CLIENT,			///< Client area hit, should pass the event on.
			HIT_TITLE,			///< Title area hit, indicate remove or start moving
			HIT_SIZENW,			///< Size North West
			HIT_SIZEN,			///< Size North
			HIT_SIZENE,			///< Size North East
			HIT_SIZEE,			///< Size East
			HIT_SIZESE,			///< Size South East
			HIT_SIZES,			///< Size South
			HIT_SIZESW,			///< Size South West
			HIT_SIZEW			///< Size West
		};
		class Surface
		{
			public:
									Surface(int width, int height);
									~Surface();
				void				drawRect(int x, int y, int width, int height, SDL_Color color);
				void				drawFillRect(int x, int y, int width, int height, SDL_Color color);
				void				drawLine(int x1, int y1, int x2, int y2, SDL_Color color);
				void				drawSurface(int x, int y, Surface* surface);
				void				setPixel(int x, int y, SDL_Color color);
				SDL_Color			getPixel(int x, int y);
			private:
				 ::kore::sdl::SdlSurface*	m_surface;
				int					m_width;
				int					m_height;
		};
		class ElementBase
		{
			public:
				virtual int			hittest(int x, int y) = 0;
				virtual Surface*	draw() = 0;
				int					width;
				int					height;
				int					left;
				int					top;
				map<string,ElementBase*>	children;
		};
		class Window : private ElementBase
		{
			public:
				string				title;
									Window(int width, int height, int left, int top, string title);
				int					hittest(int x, int y);
				Surface*			draw();

		};

	};

	namespace event
	{
		/**
		 * Event types
		 */
		enum EEventType
		{
			KEV_NULL,			///< No event
			KEV_KEYEVENT,		///< Keyboard event
			KEV_BINDEVENT,		///< KoreBind event
			KEV_MOUSEEVENT,		///< Mouse event
			KEV_HITTEST			///< Hittesting event (for UI)
		};


		/**
		 * Kore event structure
		 */
		struct KoreEvent
		{
			EEventType			type;	///< Event type
			Uint32				x;		///< X coordinate of event
			Uint32				y;		///< Y coordinate of event
			Uint32				z;		///< Reserved
			Uint32				a;		///< First event parameter
			Uint32				b;		///< Second event parameter
			Uint32				c;		///< Third event parameter
			Uint32				button;	///< The button(s) involved
		};

		class Factory
		{
			public:
				KoreEvent*			create();
		};

		class Consumer
		{
			public:
									Consumer();
									~Consumer();
				int					attachEvent(EEventType event);
				int					detachEvent(EEventType event);
				void				detachAllEvents();
		};
	};

};

#endif // __KORE2_H
